﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.ColorSpaces;

namespace LowLevelGraphics.Filter
{
    public enum CHANNEL_MODE
    {
        MIN = 1,
        MAX = 2,
        AVERAGE = 4,
        RED = 8,
        GREEN = 16,
        BLUE = 32,
        MID = 64,
        MIN_HISTOGRAM = 128,
        MAX_HISTOGRAM = 256
    }

    //TODO: extend this for all color spaces.
    /// <summary>
    /// Calculate Min or Max of pixels by using different functions
    /// The result is always a gray level picture.
    /// </summary>
    public class ChannelMinMax : BaseImageFilter
    {
        private delegate Color ChannelMinMaxColorFunction(Color _color);

        protected CHANNEL_MODE M_MODE = CHANNEL_MODE.MIN;
        protected AbstractColorSpace m_ColorSpace = new ColorSpaces.RGB(Color.Black);

        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelMinMax"/> class.
        /// </summary>
        public ChannelMinMax()
            : this(CHANNEL_MODE.MIN)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelMinMax"/> class.
        /// </summary>
        /// <param name="_MODE">The _ MODE.</param>
        public ChannelMinMax(CHANNEL_MODE _MODE)
        {
            M_MODE = _MODE;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelMinMax"/> class.
        /// </summary>
        /// <param name="_channelMinMax">The _channel min max.</param>
        internal ChannelMinMax(ChannelMinMax _channelMinMax)
        {
            this.M_MODE = _channelMinMax.M_MODE;
        }

        /// <summary>
        /// Gets or sets the channel mode.
        /// </summary>
        /// <value>The channel mode.</value>
        public CHANNEL_MODE ChannelMode
        {
            get { return M_MODE; }
            set { M_MODE = value; }
        }

        /// <summary>
        /// Gets or sets the color space.
        /// </summary>
        /// <value>The color space.</value>
        public AbstractColorSpace ColorSpace
        {
            get { return m_ColorSpace; }
            set { m_ColorSpace = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            ChannelMinMaxColorFunction colorFunction = null;

            int y = 0;
            int x = 0;

            switch (M_MODE)
            {
                case CHANNEL_MODE.MIN:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.MinChannelColor);
                    break;

                case CHANNEL_MODE.MAX:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.MaxChannelColor);
                    break;

                case CHANNEL_MODE.AVERAGE:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.ChannelAverageColor);
                    break;

                case CHANNEL_MODE.BLUE:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.ExtractBlueColor);
                    break;

                case CHANNEL_MODE.GREEN:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.ExtractGreenColor);
                    break;

                case CHANNEL_MODE.RED:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.ExtractRedColor);
                    break;

                case CHANNEL_MODE.MID:
                    colorFunction = new ChannelMinMaxColorFunction(ColorFunctions.MidChannelColor);
                    break;

                case CHANNEL_MODE.MAX_HISTOGRAM:
                case CHANNEL_MODE.MIN_HISTOGRAM:

                    //What does this do?
                    //Create a histogram for each rgb channel separately.
                    //and remember max count
                    //the more seldom the color, the darker it will appear afterwards
                    byte r = 0;
                    byte g = 0;
                    byte b = 0;
                    byte a = 0;
                    int[] aValueR = new int[256];
                    int[] aValueG = new int[256];
                    int[] aValueB = new int[256];

                    int nMaxCount = 0;
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            _bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            aValueR[r]++;
                            aValueG[g]++;
                            aValueB[b]++;
                            if (aValueR[r] > nMaxCount) nMaxCount = aValueR[r];
                            if (aValueG[g] > nMaxCount) nMaxCount = aValueG[g];
                            if (aValueB[b] > nMaxCount) nMaxCount = aValueB[b];
                        }
                    }

                    //
                    int nValue = 0;
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            _bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            //take the min value of each channel

                            if (M_MODE == CHANNEL_MODE.MIN_HISTOGRAM)
                            {
                                nValue = Math.Min(aValueB[b], Math.Min(aValueR[r], aValueG[g]));
                            }
                            else if (M_MODE == CHANNEL_MODE.MAX_HISTOGRAM)
                            {
                                nValue = Math.Max(aValueB[b], Math.Max(aValueR[r], aValueG[g]));
                            }

                            //normalize values
                            float fValue = (float)nValue / nMaxCount * 255.0f;
                            bitmap.SetPixelDirect(x, y, (byte)fValue, (byte)fValue, (byte)fValue);
                        }
                    }
                    return _bitmap;

            }

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    Color color = bitmap.GetPixel(x, y);

                    color = colorFunction(color);
                    bitmap.SetPixel(x, y, color);
                }
            }

            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ChannelMinMax(this);
        }
    }
}
